package br.senai.sc.dao;

import java.io.Serializable;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.FlushModeType;
import javax.persistence.Persistence;
import javax.persistence.Query;

import br.senai.sc.util.ObjectToSearch;

public class GenericDAOImp<T, ID extends Serializable> implements
		GenericDAO<T, ID> {
	private EntityManager em;
	private EntityManagerFactory factory = null;
	private EntityManager entityManager = null;

	public GenericDAOImp() {
		this.em = getEntityManager();
	}

	private EntityManager getEntityManager() {
		try {
			if (factory == null || em == null || !factory.isOpen()
					|| !em.getTransaction().isActive()) {

				factory = Persistence.createEntityManagerFactory("JPA");
				// Cria um entity manager.
				entityManager = factory.createEntityManager();
			}
			// Fecha o factory para liberar os recursos utilizado.
			return entityManager;
		} catch (Exception ex) {
			ex.printStackTrace();
			return null;

		}
	}

	@Override
	public <T> void create(T entity) {
		EntityTransaction t = em.getTransaction();
		try {
			if (t == null) {
				em = getEntityManager();
				t = em.getTransaction();
			}
			t.begin();
			em.persist(entity);
			t.commit();
		} catch (Exception ex) {
			if (t.isActive()) {
				em.getTransaction().rollback();
			}
			ex.printStackTrace();
		}
	}

	@Override
	public <T> void merge(T entity) {
		EntityTransaction t = em.getTransaction();
		try {
			if (t == null) {
				em = getEntityManager();
				t = em.getTransaction();
			}
			t.begin();
			em.clear();
			em.merge(entity);
			t.commit();
		} catch (Exception ex) {
			if (t.isActive()) {
				em.getTransaction().rollback();
			}
			ex.printStackTrace();
		}

	}

	@Override
	public <T> void delete(T entity) {
		EntityTransaction t = em.getTransaction();
		try {
			if (t == null) {
				em = getEntityManager();
				t = em.getTransaction();
			}
			t.begin();
			em.remove(entity);
			t.commit();
		} catch (Exception ex) {
			if (t.isActive()) {
				em.getTransaction().rollback();
			}
			ex.printStackTrace();
		}

	}

	@SuppressWarnings("unchecked")
	@Override
	public <T> T findById(Class<T> clazz, ID id) {
		T retorno = null;
		EntityTransaction t = em.getTransaction();
		if (t == null) {
			em = getEntityManager();
			t = em.getTransaction();
		}
		try {
			t.begin();
			retorno = (T) em.find(clazz, id);
			t.commit();
		} catch (Exception ex) {
			if (t.isActive()) {
				t.rollback();
			}
			ex.printStackTrace();
		}
		return (retorno != null ? retorno : null);

	}

	@SuppressWarnings("rawtypes")
	@Override
	public <T> List<T> listAll(Class<T> clazz) {
		EntityTransaction t = em.getTransaction();
		if (t == null) {
			em = getEntityManager();
			t = em.getTransaction();
		}
		List retorno = null;
		try {
			String query = "select o from " + clazz.getSimpleName() + "  o ";
			Query q = em.createQuery(query);
			retorno = q.getResultList();
			em.clear();
		} catch (Exception ex) {
			ex.printStackTrace();
		}
		return (!retorno.isEmpty() ? (List<T>) retorno : null);
	}

	@Override
	public <T> T listByProperty(Class<T> clazz, String propertyName,
			Object propertyValue) {
		List retorno = null;
		EntityTransaction t = em.getTransaction();
		if (t == null) {
			em = getEntityManager();
			t = em.getTransaction();
		}
		try {
			String query = "select o from " + clazz.getSimpleName()
					+ "  o  where o." + propertyName + "  = :propertyValue ";
			Query q = em.createQuery(query).setParameter("propertyValue",
					propertyValue);
			retorno = q.getResultList();
			em.clear();
		} catch (Exception ex) {
			ex.printStackTrace();
		}
		return (!retorno.isEmpty() ? (T) retorno.get(0) : null);
	}

	@Override
	public <T> List<T> listAllByProperty(Class<T> clazz, String propertyName,
			Object propertyValue) {
		List<?> retorno = null;
		EntityTransaction t = em.getTransaction();
		if (t == null) {
			em = getEntityManager();
			t = em.getTransaction();
		}
		try {
			String query = "";
			em.setFlushMode(FlushModeType.AUTO);
			if (propertyName.equals("titulo")) {
				query = "select o from " + clazz.getSimpleName()
						+ "  o  where o." + propertyName
						+ "  LIKE :propertyValue ";
			} else {
				query = "select o from " + clazz.getSimpleName()
						+ "  o  where o." + propertyName
						+ "  = :propertyValue ";
			}
			Query q = em.createQuery(query).setParameter("propertyValue",
					propertyValue);
			retorno = q.getResultList();
			em.clear();
		} catch (Exception ex) {
			ex.printStackTrace();
		}

		return (!retorno.isEmpty() ? (List<T>) retorno : null);
	}

	@Override
	public <T> List<T> listAllByProperties(Class<T> clazz,
			ObjectToSearch... search) {
		List<?> retorno = null;
		EntityTransaction t = em.getTransaction();
		if (t == null) {
			em = getEntityManager();
			t = em.getTransaction();
		}
		try {
			String query = "";
			em.setFlushMode(FlushModeType.AUTO);
			query = "SELECT o FROM " + clazz.getSimpleName() + " o WHERE ";
			int count = 0;
			for (ObjectToSearch ob : search) {
				if (count != 0) {
					query += " AND ";
				}
				query += "o." + ob.getPropertyName() + " = "
						+ ob.getPropertyValue();
				count++;
			}
			Query q = em.createQuery(query);
			retorno = q.getResultList();
			em.clear();
		} catch (Exception ex) {
			ex.printStackTrace();
		}

		return (!retorno.isEmpty() ? (List<T>) retorno : null);
	}

}
